
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class AnalizadorLexico
{

	private int fila;
	private int columna;
	private String actual;
	private String cola;
	private RandomAccessFile entrada;

	public AnalizadorLexico(String path)
	{
		fila = 1;
		columna = 0;
		cola = "";
		try
		{
			entrada = new RandomAccessFile(path, "r");
		} catch (FileNotFoundException ex)
		{
			System.err.print("Error. El fichero de entrada no existe");
		}
	}

	private char NextChar() throws IOException
	{
		byte b;
		char c;
		if (cola.isEmpty())
		{
			b = entrada.readByte();
			c = (char) b;
		} else
		{
			c = cola.charAt(0);
			cola = cola.substring(1);
		}
		return c;
	}

	/**
	 * Obtiene el siguiente token y lo procesa
	 * @return Token
	 * @throws Excepcion 
	 */
	public Token NextToken() throws Excepcion
	{
		char c = ' ', aux;
		int f = fila;
		int k = columna;
		boolean leidoPunto = false, leidoNumero = false;
		actual = "";
		try
		{
			c = NextChar();
			columna++;
			if (c == -1)
			{
				return new Token(Token.EOF);
			}
			//Eliminamos vacios delante
			while (c == '\n' || c == ' ' || c == '\t')
			{
				if (c == '\n')
				{
					columna = 1;
					fila++;
				} else
				{
					columna++;
				}
				c = NextChar();
			}
			f = fila;
			k = columna;
			//Procesamos simbolos
			switch (c)
			{
				case '(':
					return new Token(Token.PARI, f, k);
				case ')':
					return new Token(Token.PARD, f, k);
				case ';':
					return new Token(Token.PYC, f, k);
				case '=':
					return new Token(Token.ASIG, f, k);
				case '{':
					return new Token(Token.LLAVEI, f, k);
				case '}':
					return new Token(Token.LLAVED, f, k);
				case '+':
					return new Token("+", Token.OPSUM, f, k);
				case '-':
					return new Token("-", Token.OPSUM, f, k);
			}
			//Procesamos numeros
			if (c >= '0' && c <= '9')
			{
				leidoPunto = false;
				while (c >= '0' && c <= '9' || c == '.')
				{
					actual += c;
					if (c != '.')
					{
						leidoNumero = true;
					} else
					{
						if (leidoPunto == false)
						{
							leidoNumero = false;
							leidoPunto = true;
						} else
						{
							throw (new Excepcion("Error 1 (" + fila + "," + columna + "): caracter '.' incorrecto"));
						}
					}
					columna++;
					c = NextChar();
				}

				if (!leidoNumero)
				{
					cola += ".";
					actual = actual.substring(0, actual.length() - 1);
					columna--;
				}

				if (c != ' ' || c != '\t' || c != '\n')
				{
					columna--;
					cola += c;
				}

				try
				{
					Integer.parseInt(actual);
				} catch (NumberFormatException exInt)
				{
					try
					{
						Double.parseDouble(actual);
					} catch (NumberFormatException exDouble)
					{
						throw (new Excepcion("Error 1 (" + fila + "," + columna + "): caracter '" + actual.charAt(actual.length() - 1) + "' incorrecto"));
					}
					return new Token(actual, Token.REAL, f, k);
				}
				return new Token(actual, Token.ENTERO, f, k);
			}

			if (c == '/')
			{
				actual += c;

				c = NextChar();
				if (c != '*')
				{
					cola+=c;
					return new Token("/", Token.OPMUL, f, k);
				}
				actual += c;
				columna++;

				while (actual.length() < 4 || !(actual.charAt(actual.length() - 2) == '*' && actual.charAt(actual.length() - 1) == '/'))
				{
					c = NextChar();

					if (c == '\n')
					{
						columna = 0;
						fila++;
					} else
					{
						columna++;
					}

					actual += c;
				}
				//return new Token(actual, Token.COMENTARIO, fila, columna );
				f = fila;
				k = columna;
				return NextToken();
			}

			if (c == '*')
			{
				return new Token("*", Token.OPMUL, f, k);
				//return new Token(Token.OPMUL, f, k);
			}

			//Procesamos id,double,int o main
			if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
			{
				throw new Excepcion("Error 1 (" + fila + "," + columna
						+ "): caracter '" + c + "' incorrecto");
			}

			while (c != ' ' && c != '\t' && c != '\n' && c != '(' && c != ')' && c != '='
					&& c != '{' && c != '}' && c != ';' && c != '/' && c != '*'
					&& ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
			{
				actual += c;
				columna++;

				c = NextChar();
			}

			cola += c;
			columna--;

			if (actual.equals(Token.Tipos[Token.DOUBLE]))
			{
				return new Token(Token.DOUBLE, f, k);
			}
			if (actual.equals(Token.Tipos[Token.INT]))
			{
				return new Token(Token.INT, f, k);
			}
			if (actual.equals(Token.Tipos[Token.MAIN]))
			{
				return new Token(Token.MAIN, f, k);
			}

			return new Token(actual, Token.ID, f, k);
		} catch (EOFException ex)
		{

			if (actual.equals(""))
			{
				return new Token(actual, Token.EOF, f, k);
			} else
			{
				if (actual.charAt(0) == '/')
				{
					if (actual.length() > 2 && actual.charAt(1) == '*'
							&& actual.charAt(actual.length() - 2) == '*' && actual.charAt(actual.length() - 1) == '/')
					{
						return NextToken();
					}
					throw new Excepcion("Error 2: fin de fichero inesperado");
				}
				if (actual.charAt(0) >= '0' && actual.charAt(0) <= '9')
				{
					try
					{
						Integer.parseInt(actual);
					} catch (NumberFormatException exInt)
					{
						try
						{
							Double.parseDouble(actual);
						} catch (NumberFormatException exDouble)
						{
							throw (new Excepcion("Error 1 (" + fila + "," + columna + "): caracter '" + actual.charAt(actual.length() - 1) + "' incorrecto"));
						}
						return new Token(actual, Token.REAL, f, k);
					}
					return new Token(actual, Token.ENTERO, f, k);
				}
				if (actual.equals(Token.Tipos[Token.DOUBLE]))
				{
					return new Token(Token.DOUBLE, f, k);
				}
				if (actual.equals(Token.Tipos[Token.INT]))
				{
					return new Token(Token.INT, f, k);
				}
				if (actual.equals(Token.Tipos[Token.MAIN]))
				{
					return new Token(Token.MAIN, f, k);
				}

				return new Token(actual, Token.ID, f, k);
			}
			//throw (newExcepcion("Error (" + fila + " , " + columna + "): caracter " + actual.charAt(actual.length()-1) + "  incorrecto"));
		} catch (IOException ex)
		{
			throw new Excepcion("Fallo de entrada/Salida");
		}
	}
}

